#pragma once

#include <iostream>
#include <string>
#include <memory>

#include "Socket.hpp"
#include "TcpServer.hpp"
#include "InetAddr.hpp"
#include "HttpProtocol.hpp"

using namespace My_Socket;

using task_t = std::function<void()>;

const std::string defhomepage = "wwwlsh";
const std::string firstpage = "index.html";
const std::string page404 = "wwwroot/404.html";

class HttpServer
{
private:
    std::string Suffix2Desc(const std::string &suffix)
    {
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".jpg")
            return "application/x-jpg";
        else
            return "text/html";
    }
    std::string GetContent(const std::string path)
    {
        std::string content;
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
            return std::string();
        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);

        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        LOG(LogLevel::DEBUG) << "content length: " << content.size();
        return content;
    }
    bool ProcessRequest(HttpRequest &req, HttpResponse &resq)
    {
        std::string path = defhomepage + req.GetPath();
        if (path.back() == '/')
        {
            path += firstpage; // wwwlsh/index.html
        }

        LOG(LogLevel::DEBUG) << "------客户端在请求: " << req.GetUri();
        req.LOGPrint();
        LOG(LogLevel::DEBUG) << "-----------------------------------" << req.GetUri();

        std::string content = GetContent(path);
        if (content.empty())
        {
            resq.SetStatusCode("404");
            content = GetContent(page404);
        }
        else
        {
            resq.SetHeardKV("Content-Type", std::to_string(content.size()));
            resq.SetStatusCode("200");
        }
        resq.SetHeardKV("Content-Length", std::to_string(content.size()));
        auto pos = req.GetUri().rfind(".");

        std::string mime_type;
        if (pos == std::string::npos)
            mime_type = std::string(".html");
        else
            mime_type = req.GetUri().substr(pos);
        resq.SetHeardKV("Content-Type", mime_type);

        resq.SetBody(content);

        return true;
    }
    void HanderRequest(SockPtr &sockptr, InetAddr &cliaddr)
    {
        std::string recvstr;
        while (true)
        {
            int n = sockptr->Recv(recvstr);
            if (n < 0)
            {
                LOG(LogLevel::WARNING) << "Recv fail!";
                continue;
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client close, client# " << cliaddr.GetStrIpPort();
                break;
            }

            LOG(LogLevel::INFO) << "recvbuf is : " << recvstr;

            HttpRequest req;
            HttpResponse resq;
            req.Deserialize(recvstr);

            ProcessRequest(req, resq);

            std::string resq_str;
            resq.Serialize(resq_str);

            LOG(LogLevel::INFO) << "result is : " << resq_str;
            sockptr->Send(resq_str);
        }
    }

public:
    HttpServer(int port) : _tcpserver(std::make_unique<TcpServer>(port))
    {
    }
    void Start()
    {
        _tcpserver->InitServer([this](SockPtr sockptr, InetAddr addr)
                               { this->HanderRequest(sockptr, addr); });
        _tcpserver->Loop();
    }
    ~HttpServer()
    {
    }

private:
    std::unique_ptr<TcpServer> _tcpserver;
};